package biolean.commands;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import biolean.Utils;
import biolean.files.Annotation;
import biolean.files.AnnotationFile;
import biolean.files.BioFile;
import biolean.files.Sequence;
import biolean.files.SequenceFile;
import biolean.logic.GraphicElement;

/**
 * Prikaz "Vysekavaj".
 * Vytvori novy sekvencny subor aplikovanim vstupnych anotacii na vstupne sekvencie.
 * @author Rasto
 */

public class SubstractCmd extends Command {

	/**
	 * @param args
	 */
	public SubstractCmd()
	{
		insertAllowedFileType(SequenceFile.class, BioFile.IOTYPE.INPUT, 1);
		insertAllowedFileType(AnnotationFile.class, BioFile.IOTYPE.INPUT, 1);
		insertAllowedFileType(SequenceFile.class, BioFile.IOTYPE.OUTPUT, 1);
	}

	private final String NEW_SEQ_SUFFIX = ".part";
	private final int SEQ_BUILDER_SIZE = 1024;
	
	private AnnotationFile 	 inAnnotationFile;
	private SequenceFile 	 inSequenceFile;
	private SequenceFile 	 outSequenceFile;
	private List<Annotation> annotations;
	private boolean	connectSameSequences = false;

	protected boolean debugg = true;


	@Override
	public void processArguments()
	{
		for (GraphicElement bioFile : mInPuts)
		{
			if  (Utils.isSubClass( SequenceFile.class , bioFile.getClass() ) )
			{
				inSequenceFile = (SequenceFile) bioFile;
			}else if (Utils.isSubClass( AnnotationFile.class , bioFile.getClass() ) ){
				inAnnotationFile = (AnnotationFile) bioFile;
			}
		}
		outSequenceFile =  (SequenceFile)mConnection.get(0);
	}

	/**
	 * Spustenie prikazu.
	 */
	@Override
	public void run() throws IOException
	{
		inAnnotationFile.openReader();
		importAnnotations();
		inAnnotationFile.closeReader();	
		inSequenceFile.openReader();
		outSequenceFile.openWriter();
		processSequences();
		inSequenceFile.closeReader();
		outSequenceFile.closeWriter();
	}

	/**
	 * Nastavenie sposobu zapisu sekvencii vo vystupnom subore.
	 * Pri hodnote true sa vystupne sekvencie s rovnakym nazvom spoja do jednej.
	 * @param state - true alebo false
	 */
	public void setConnectSameSequences(boolean state)
	{
		connectSameSequences = state;
	}

	/** 
	 * Nacita anotacie do pamate a utriedi ich podla nazvu anotacie.
	 * @param annotationFile - cesta k vstupnemu anotacnemu suborom
	 */
	public void importAnnotations() throws IOException
	{
		annotations = new ArrayList<Annotation>();
		annotations =  inAnnotationFile.readFile() ;
		Collections.sort(annotations, new AnnotationComparator());
	}

	/** 
	 * Prejde vsetky sekvencie.
	 * Vyberie anotacie sekvencie a sekvenciu s jej anotaciami odosle na spracovanie.
	 */
	private void processSequences() throws IOException
	{
		Sequence seq;
		while((seq = inSequenceFile.readSeq()) != null) {
			List<Annotation> seqAnns = getSeqAnnotations(seq.getName());
			if (connectSameSequences) {
				substractToOne(seqAnns, seq);
			} else {
				substractToMany(seqAnns, seq);			
			}
		}			
	}

	/** 
	 * Vysekne nove sekvencie a zapise ich do subory ako jednu sekvenciu.
	 * Nova sekvencia ma rovnaky nazov ako povodna sekvencia.
	 * @param seqAnns - Anotacie jednej sekvencie.
	 * @param seq - Sekvencia z ktorej sa vysekava.
	 */
	private void substractToOne(List<Annotation> seqAnns, Sequence seq) throws IOException {
		StringBuilder seqData = new StringBuilder(SEQ_BUILDER_SIZE);
		for (Annotation ann: seqAnns) {
			seqData.append(seq.extract(ann.getStart(), ann.getEnd()));
		}
		outSequenceFile.writeSeq(new Sequence(seq.getName(), seqData.toString()));
	}
	
	/** 
	 * Vysekne nove sekvencie a zapise ich do suboru za sebou.
	 * Pre nove sekvencie sa vygeneruju jedinecne mena na zaklade mena povodnej sekvencie.
	 * Ak je nova sekvencia len jedna, nova bude mat nazov povodnej sekvencie.
	 * @param seqAnns - Anotacie jednej sekvencie.
	 * @param seq - Sekvencia z ktorej sa vysekava.
	 */
	private void substractToMany(List<Annotation> seqAnns, Sequence seq) throws IOException {
		String seqName = seq.getName();
		int counter = 0;
		for (Annotation ann: seqAnns) {
			counter++;
			if (seqAnns.size() > 1) {
				seqName = seq.getName() + NEW_SEQ_SUFFIX + Integer.toString(counter);
			}
			String seqData = seq.extract(ann.getStart(), ann.getEnd());
			outSequenceFile.writeSeq(new Sequence(seqName, seqData));
		}						
	}

	/**
	 * Vrati anotacie s danym menom sekvencie, ak existuju.
	 * @return Zoznam anotacii.
	 */
	private List<Annotation> getSeqAnnotations(String seqName) 
	{
		List<Annotation> anns = new ArrayList<Annotation>();
		for (Annotation annModel: annotations) 
		{
			if (annModel.getSeqname().equals(seqName)) 
			{
				anns.add(annModel);
			}
			if (annModel.getSeqname().compareTo(seqName) > 0)
			{
				break;
			}
		}
		return anns;
	}

	/** 
	 * Trieda pre porovnanie nazvov anotacii.
	 */
	private class AnnotationComparator implements Comparator<Annotation> 
	{
		public int compare(Annotation ann1, Annotation ann2) 
		{
			return ann1.getSeqname().compareTo(ann2.getSeqname());
		}
	}

	@Override
	public void loadArgument(String name, String value)
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public void performDoubleClick()
	{
		// TODO Auto-generated method stub
		
	}
	
	public static String getDescription()
	{
		return "<html>"
                + "Toto je descpition"+"<br>"
                + "Line 2"
                + "</html>";
	}
}

